package lang;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * 懒汉式双检锁[推荐用]
 * <p>
 * 这种方式跟饿汉式方式采用的机制类似，但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。
 * 不同的地方在饿汉式方式是只要 Singleton 类被装载就会实例化，没有懒加载的作用，
 * 而静态内部类方式在 Singleton 类被装载时并不会立即实例化，而是在需要实例化时，
 * 调用 getInstance 方法，才会装载 SingletonInstance 类，从而完成 Singleton 的实例化。
 * <p>
 * 类的静态属性只会在第一次加载类的时候初始化，所以在这里，JVM 帮助我们保证了线程的安全性，
 * 在类进行初始化时，别的线程是无法进入的。
 * <p>
 * 优点：避免了线程不安全，延迟加载，效率高
 * ————————————————
 */
public class SingL5 {

    private static boolean flag = false;


    private static volatile SingL5 singL5;

    private SingL5() {
        synchronized (SingL5.class) {
            if (flag == false) {
                flag = true;
            } else {
                throw new RuntimeException("不要试图用反射破解单例");
            }
        }
    }

    public static synchronized SingL5 getSingL5() {
        if (singL5 == null) {
            synchronized (SingL5.class) {
                if (singL5 == null) {
                    singL5 = new SingL5();
                }
            }

        }
        return singL5;
    }


    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //如果获取了该标志位的属性，还是可以通过反射篡改该private属性
        Field flag = SingL5.class.getDeclaredField("flag");
        flag.setAccessible(true);   //篡改属性
        Constructor<SingL5> declaredConstructor= SingL5.class.getDeclaredConstructor();   //获取无参构造
        declaredConstructor.setAccessible(true);
        SingL5 l1 = declaredConstructor.newInstance(); // 通过newInstance方法新建对象
        flag.set(l1,false);
        System.out.println(l1.hashCode());  //后台输出该对象的哈希值
        SingL5 l2 = SingL5.getSingL5();  //通过传统的方式创建，不通过反射创建
        System.out.println(l2.hashCode());  //后台输出正常创建对象的哈希值，输出结果为两者不一致
    }

}
